﻿//------------------------------------------------------------
// shaco Framework
// Copyright © 2017-2021 chang.liu All rights reserved.
// Feedback: 449612236@qq.com
//------------------------------------------------------------

namespace shaco.Base.Net
{
    /// <summary>
    /// 网络套接字，主要负责网络数据传输使用
    /// </summary>
    public class SocketTCP : ISocket
    {
        private enum ConnectResult
        {
            None,
            Connecting,
            Success,
            Failed
        }

        private System.Net.Sockets.TcpClient _socket = null;
        private System.Net.Sockets.NetworkStream _socketStream = null;
        private string _host = string.Empty;
        private int _port = -1;
        private ConnectResult _connectResult = ConnectResult.None;
        private bool _abortConnect = false;

        /// <summary>
        /// 连接网络
        /// <param name="host">主机地址</param>
        /// <param name="port">主机端口号</param>
        /// <param name="connectTimeoutSeconds">连接超时时间</param>
        /// <param name="callback">连接结果回调</param>
        /// </summary>
        public void Connect(string host, int port, float connectTimeoutSeconds, System.Action<bool> callback)
        {
            if (null != _socket)
            {
                Disconnect();
            }

            var addresses = System.Net.Dns.GetHostAddresses(host);
            if (null == addresses || 0 == addresses.Length)
            {
                Log.Error("SocketTCP Connect error: invalid host=" + host + " port=" + port);
                return;
            }

            if (addresses[0].AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
            {
                _socket = new System.Net.Sockets.TcpClient(System.Net.Sockets.AddressFamily.InterNetworkV6);
            }
            else
            {
                _socket = new System.Net.Sockets.TcpClient(System.Net.Sockets.AddressFamily.InterNetwork);
            }

            Log.Info("SocketTCP Connect start, host=" + host + " port=" + port);

            _host = host;
            _port = port;
            _abortConnect = false;
            _connectResult = ConnectResult.None;

            //等待连接结果返回
            if (null != callback)
            {
                Base.WaitFor.Run(() =>
                {
                    return _connectResult == ConnectResult.Success || _connectResult == ConnectResult.Failed || _abortConnect;
                }, () =>
                {
                    if (null != callback && !_abortConnect)
                        callback(_connectResult == ConnectResult.Success);

                    _abortConnect = false;
                });
            }
            ThreadPool.RunThread(ThreadConnect);
        }

        private void ThreadConnect()
        {
            try
            {
                _connectResult = ConnectResult.Connecting;
                _socket.NoDelay = true;

                if (!_abortConnect)
                {
                    _socket.Connect(_host, _port);

                    Log.Info("SocketTCP ThreadConnect start, connected=" + _socket.Connected + " host=" + _host + " port=" + _port);
                    if (_socket.Connected)
                    {
                        _socketStream = _socket.GetStream();
                        _connectResult = ConnectResult.Success;
                    }
                    else
                    {
                        Disconnect();
                        _connectResult = ConnectResult.Failed;
                    }
                }
            }
            catch (System.Exception e)
            {
                Log.Error("SocketTCP ThreadConnect error: host=" + _host + " port=" + _port + " e=" + e);
                _socket = null;
                _connectResult = ConnectResult.Failed;
            }
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        public void Disconnect()
        {
            if (null != _socketStream)
            {
                _socketStream.Close();
                _socketStream = null;
            }

            if (null == _socket)
            {
                Log.Error("SocketTCP Disconnect error: socket is not connected");
            }
            else
            {
                Log.Info("SocketTCP Disconnected");

                if (_socket.Connected)
                    _socket.Close();
                _socket = null;
            }

            _abortConnect = true;
        }

        /// <summary>
        /// 是否已经连接成功
        /// </summary>
        public bool IsConnected()
        {
            return null != _socket && _socket.Connected;
        }

        /// <summary>
        /// 是否有接收数据
        /// </summary>
        public bool IsDataAvailable()
        {
            return null != _socketStream && _socketStream.DataAvailable;
        }

        /// <summary>
        /// 发送数据
        /// <param name="data">二进制数据</param>
        /// <return>发送的二进制数据数量，如果返回-1表示发送失败</return>
        /// </summary>
        public int Send(byte[] data)
        {
            if (!IsConnected())
            {
                Log.Error("SocketTCP Send erorr: socket not connected");
                return -1;
            }

            if (null == data)
            {
                Log.Error("SocketTCP Send erorr: data is invalid");
                return -1;
            }

            try
            {
                _socketStream.Write(data, 0, data.Length);
                return data.Length;
            }
            catch (System.Exception e)
            {
                Log.Error("SocketTCP Send erorr: e=" + e + " data=" + data.ToStringArray());
                return -1;
            }
        }

        /// <summary>
        /// 接收数据
        /// <param name="receiveBuffer">接收到的二进制数据</param>
        /// <param name="offset">偏移位置</param>
        /// <return>接收的二进制数据数量，如果返回-1表示接收失败</return>
        /// </summary>
        public int Receive(byte[] receiveBuffer, int offset)
        {
            if (!IsConnected())
            {
                Log.Error("SocketTCP Receive erorr: socket not connected");
                return -1;
            }

            if (!_socketStream.DataAvailable)
                return 0;

            try
            {
                int receveLength = _socketStream.Read(receiveBuffer, offset, receiveBuffer.Length - offset);
                if (receveLength <= 0)
                {
                    Log.Error("SocketTCP Receive erorr: receive nothing");
                    return -1;
                }
                else
                {
                    return receveLength;
                }
            }
            catch (System.Exception e)
            {
                Log.Error("SocketTCP Receive erorr: e=" + e + " data=" + receiveBuffer.ToStringArray());
                return -1;
            }
        }

        /// <summary>
        /// <param name="send">发送超时时间(单位：秒)</param>
        /// <param name="receive">接收超时时间(单位：秒)</param>
        /// </summary>
        public void SetTimeoutSeconds(float send, float receive)
        {
            if (null == _socket)
            {
                Log.Error("SocketTCP SetTimeoutSeconds erorr: socket not connected");
                return;
            }

            _socket.SendTimeout = (int)(send * 1000);
            _socket.ReceiveTimeout = (int)(receive * 1000);
        }
    }
}